package org.gap.wizards.world.builder;

import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;

import org.gap.jseed.SeedContainer;
import org.gap.wizards.Start;
import org.gap.wizards.creature.Animal;
import org.gap.wizards.creature.AnimalBrain;
import org.gap.wizards.creature.AnimalFactory;
import org.gap.wizards.world.Wilderness;

public class WildernessBuilder {
	enum Strategy {
		Light(35, 1), Dense(100, 3);
		private final int seedCount;
		private final int chanceToPlant;

		Strategy(int seedCount, int chanceToPlant) {
			this.seedCount = seedCount;
			this.chanceToPlant = chanceToPlant;
		}

		public int seedCount() {
			return seedCount;
		}

		public boolean shouldPlant() {
			return rnd.nextInt(6) < chanceToPlant;
		}

		public boolean inForest() {
			return rnd.nextInt(8) < chanceToPlant * 2;
		}
	}

	private static List<Rectangle> quadrantsToPlaceBuildings;
	static {
		quadrantsToPlaceBuildings = new ArrayList<Rectangle>(6);
		int firstX = Start.SCREEN_WIDTH / 3 - 10;
		int secondX = Start.SCREEN_WIDTH / 3 * 2 - 10;
		int thirdX = Start.SCREEN_WIDTH - 10;
		int middle = Start.SCREEN_HEIGHT / 2 - 10;
		int bottom = Start.SCREEN_HEIGHT - 10;
		// first row
		quadrantsToPlaceBuildings.add(new Rectangle(2, 2, firstX, middle));
		quadrantsToPlaceBuildings.add(new Rectangle(firstX, 2, secondX, middle));
		quadrantsToPlaceBuildings.add(new Rectangle(secondX, 2, thirdX, middle));

		// second row
		quadrantsToPlaceBuildings.add(new Rectangle(2, middle, firstX, bottom));
		quadrantsToPlaceBuildings.add(new Rectangle(firstX, middle, secondX, bottom));
		quadrantsToPlaceBuildings.add(new Rectangle(secondX, middle, thirdX, bottom));
	}
	private static Random rnd = new Random();

	public static Wilderness createDenseForest() {
		Wilderness result = createWilderness();
		plantWilderness(Strategy.Dense, result);
		plantLargeGrouping(Strategy.Dense, result);
		return result;
	}

	private static void plantLargeGrouping(Strategy strategy, Wilderness result) {
		for (int i = 0; i < strategy.ordinal() + 2; i++) {
			int xPos = withinWidth();
			int yPos = withinHeight();
			Point point = new Point(xPos, yPos);
			int distance = rnd.nextInt(6) + 4;
			for (int x = 0; x < Start.SCREEN_WIDTH; x++) {
				for (int y = 0; y < Start.SCREEN_HEIGHT; y++) {
					if (new Point(x, y).distance(point) < distance) {
						tryToAddTree(result, strategy, x, y);
					}
				}
			}
		}
	}

	private static Wilderness createWilderness() {
		Wilderness result = new Wilderness();
		result.setDimensions(new Dimension(Start.SCREEN_WIDTH, Start.SCREEN_HEIGHT));
		return result;
	}

	public static Wilderness createLightForest(SeedContainer store) {
		Wilderness result = createWilderness();
		plantWilderness(Strategy.Light, result);
		plantLargeGrouping(Strategy.Light, result);
		addBuildings(Strategy.Light, result);

		result.setCharacterLocation(new Point(60, 20));
		addWildAnimals(store, result, Strategy.Light);

		return result;
	}

	private static void addWildAnimals(SeedContainer store, Wilderness result, Strategy strategy) {
		int count = new Random().nextInt(strategy.seedCount / 4) + 1;
		for (int i = 0; i < count; i++) {
			Animal cat = AnimalFactory.createCat(store.create(AnimalBrain.class));
			Rectangle dimensions = new Rectangle(Start.SCREEN_WIDTH, Start.SCREEN_HEIGHT);
			Point point = randomPoint(dimensions);
			while (!result.canMoveTo(point)) {
				point = randomPoint(dimensions);
			}
			cat.setLocation(point);
			result.addCreature(cat);
		}
	}

	private static void addBuildings(Strategy light, Wilderness result) {
		int numberOfBuildings = rnd.nextInt(6) - light.ordinal() + 1 * 2;
		Set<Rectangle> selected = new HashSet<Rectangle>();
		for (int i = 0; i < numberOfBuildings; i++) {
			int quadrantSelected = rnd.nextInt(quadrantsToPlaceBuildings.size());
			selected.add(quadrantsToPlaceBuildings.get(quadrantSelected));
		}
		for (Rectangle each : selected) {
			int width = rnd.nextInt(5) + 4;
			int height = rnd.nextInt(3) + 4;
			Point point = randomPoint(each);
			result.addClosedSpace(point.x, point.y, width, height, placeDoor(point.x, point.y, width, height));
		}
	}

	private static Point randomPoint(Rectangle each) {
		int x = rnd.nextInt(each.width - each.x) + each.x + 1;
		int y = rnd.nextInt(each.height - each.y) + each.y + 1;
		Point point = new Point(x, y);
		return point;
	}

	private static Point placeDoor(int x, int y, int width, int height) {
		int doorX = x + rnd.nextInt(width - 2) + 1;
		int doorY = y + rnd.nextInt(height - 2) + 1;

		switch (rnd.nextInt(4)) {
			case 0:
				return new Point(doorX, y);
			case 1:
				return new Point(x, doorY);
			case 2:
				return new Point(doorX, y + height - 1);
			default:
				return new Point(x + width - 1, doorY);
		}
	}

	private static void plantWilderness(Strategy strategy, Wilderness openField) {
		openField.setDimensions(new Dimension(Start.SCREEN_WIDTH, Start.SCREEN_HEIGHT));
		for (int i = 0; i < strategy.seedCount(); i++) {
			int x = withinWidth();
			int y = withinHeight();
			openField.plantTree(x, y);
			tryToAddForest(openField, strategy, x - 1, y);
			tryToAddForest(openField, strategy, x, y - 1);
			tryToAddForest(openField, strategy, x + 1, y);
			tryToAddForest(openField, strategy, x, y + 1);
			tryToAddForest(openField, strategy, x - 1, y - 1);
			tryToAddForest(openField, strategy, x + 1, y + 1);
			tryToAddForest(openField, strategy, x - 1, y + 1);
			tryToAddForest(openField, strategy, x + 1, y - 1);
		}
	}

	private static void tryToAddTree(Wilderness openField, Strategy strategy, int x, int y) {
		if (isBetween(x, 0, Start.SCREEN_WIDTH) && isBetween(y, 0, Start.SCREEN_HEIGHT) && strategy.shouldPlant()) {
			openField.plantTree(x, y);
		}
	}

	private static void tryToAddForest(Wilderness openField, Strategy strategy, int x, int y) {
		if (isBetween(x, 0, Start.SCREEN_WIDTH) && isBetween(y, 0, Start.SCREEN_HEIGHT) && strategy.inForest()) {
			openField.plantTree(x, y);
		}
	}

	private static boolean isBetween(int value, int start, int end) {
		return value >= start && value < end;
	}

	private static int withinHeight() {
		return rnd.nextInt(Start.SCREEN_HEIGHT);
	}

	private static int withinWidth() {
		return rnd.nextInt(Start.SCREEN_WIDTH);
	}
}
